Expression Subcontexts · Classes of Expressions · Class Conversions · Type Conversions · Promoting · Balancing · Assigning and Type Casting · Changing Representations · Pointer Arithmetic · Reading Expressions · Grouping · Operator Summary · Order of Evaluation
Add Assign · Add · Address of · Assignment · Bitwise AND Assign · Bitwise AND · Bitwise Exclusive OR Assign · Bitwise Exclusive OR · Bitwise Inclusive OR Assign · Bitwise Inclusive OR · Bitwise NOT · Comma · Conditional · Divide Assign · Divide · Equals · Function Call · Greater Than Or Equal · Greater Than · Indirection · Left Shift Assign · Left Shift · Less Than Or Equal · Less Than · Logical AND · Logical NOT · Logical OR · · Multiply Assign · Multiply · Not Equals · Plus · Point at · Postdecrement · Postincrement · Predecrement · Preincrement · Remainder Assign · Remainder · Right Shift Assign · Right Shift · Select · sizeof · Subscript · Subtract Assign · Subtract · Type Cast
You write expressions to determine values, to alter values stored in objects, and to call functions that perform input and output. In fact, you express all computations in the program by writing expressions.
The translator itself must evaluate some of the expressions you write to determine properties of the program. The translator or the target environment must evaluate other expressions prior to program startup to determine the initial values stored in objects with static duration. The program evaluates the remaining expressions when it executes.
This document describes the different classes of expressions and the restrictions on each class. It presents the common rules for writing all expressions, determining their types, and computing their values. It also discusses the constraints on the flow of control through an expression.
Within a statement or declaration, every full expression that you write inhabits one of three contexts:
More generally, however, every full expression or subexpression that you write inhabits one of four expression subcontexts, depending on its goal:
An rvalue subcontext specifies a value that has an object type other than an array type. You create an rvalue subcontext wherever you need to specify a test or value to the translator, determine an initial value prior to program startup or compute a value when the program executes.
An lvalue subcontext designates an object, but its expression can have either an object type or an incomplete type. You create an lvalue subcontext wherever you need to access the value stored in an object, alter the stored value, or determine the address of the object. (If the type is incomplete, you can determine only the address of the object.)
A function-designator subcontext designates a function. Hence, its expression has a function type. You create a function-designator subcontext wherever you need to call a function or determine its address.
A side-effects subcontext specifies no value and designates no object or function. Hence, its expression can have a void type. You create a side-effects subcontext when you need to cause only side effects.
For example, consider the following code sequence:
void f(int); int x; f(x = 3);
In the last line:
f
is in a function-designator subcontextx
is in an lvalue subcontext3
and x = 3
are both in rvalue subcontextsf(x = 3)
is in a side-effects subcontextEvery expression that you write belongs to one of several expression classes, depending upon its form. Four of these classes are closely associated with the four expression subcontexts:
The first two of these classes have a number of subclasses. For instance, an arbitrary rvalue expression can be evaluated only by the program when it executes. One connotation of the term ``rvalue expression'' is that you cannot write such an expression where the translator must be able to determine its value before the program executes. Four subclasses of rvalue expressions, however, have a value that the translator or the target environment can determine prior to program startup:
An address constant expression specifies a value that has a pointer type and that the translator or target environment can determine prior to program startup. Therefore, the expression must not cause side effects. You must not write subexpressions with a void type. (You cannot write a function call, assigning, or comma operator, except as part of the operand of a sizeof operator.) You write address constant expressions to specify the initial values stored in objects of pointer type with static duration.
For example:
extern int first; static int *pf = &first; &first is address constant
An arithmetic constant expression specifies a value that has an arithmetic type and that the translator or target environment can determine prior to program startup. Therefore, the expression must not cause side effects. You must write only subexpressions that have arithmetic type. (You cannot write a function call, assiging, or comma operator, except as part of the operand of a sizeof operator.) You write arithmetic constant expressions to specify the initial values stored in objects of arithmetic type with static duration.
For example:
extern int counter = 0; static int flt_bits = 6 / 0.30103 + 0.5; static int ret_bytes = sizeof f();
An integer constant expression specifies a value that has an integer type and that the translator can determine at the point in the program where you write the expression. The same restrictions apply as for arithmetic constant expressions. In addition, you must write only subexpressions that have integer type. You can, however, write a floating-point constant as the operand of an integer type cast operator. You write integer constant expressions to specify:
For example:
extern int a[20], a_copy[sizeof a / sizeof a[0]]; enum {red = 1, green = 4, blue = 16} color;
A #if expression specifies a value that can be determined by an if or elif directive. After preprocessing replaces all names within the expression, the same restrictions apply as for integer constant expressions.
For example:
#if __STDC__ && 32767 < INT_MAX
Lvalue expressions fall into one of four subclasses:
An accessible lvalue expression designates an object that has an object type other than an array type. Hence, you can access the value stored in the object.
For example:
static const struct complex imag = {0, 1}; return (imag); imag is accessible lvalue
A modifiable lvalue expression designates an object that has object type other than an array type or a const type. Hence, you can alter the value stored in the object.
For example:
static int next_no = 0; return ++next_no; next_no is modifiable lvalue
An array lvalue expression designates an object that has an array type. The type can be incomplete. You often write expressions that implicitly convert an array lvalue expression to an rvalue expression of a pointer type. You can also take the address of an array lvalue expression. For example:
static int bmask[] = {1, 8, 2, 4}; int (*pb)[] = &bmask; &bmask is pointer to array y = arg & bmask[i]; bmask is array lvalue scan_it(bmask); bmask becomes pointer to int
An incomplete non-array lvalue expression designates an object that has a void type or an incomplete structure or union type. You can only take the address of such an expression. For example:
extern struct who_knows rom; static struct who_knows *rom_base = &rom;
Many of the expression subclasses are proper subsets of other subclasses. In other cases, the translator can convert an expression of one class to another when the context demands it. The following diagram illustrates all classes and subclasses of expressions and how they relate. Each box denotes a different class or subclass. It contains a subset of all expressions that you can write. An unlabeled arrow connects each subset to its containing set.
For example, an integer constant expression is a subset of all arithmetic constant expressions, which in turn is a subset of all rvalue expressions. An incomplete non-array lvalue expression is not a subset of any other. A label on an arrow tells you that a conversion occurs if you write an expression of one class where the context requires a result of another class. For example, an rvalue expression that you write in a side-effects subcontext becomes a void result by dropping the value associated with the rvalue.
The translator can make four conversions to satisfy context:
If you write an rvalue expression in a side-effects subcontext, the translator discards the value of the expression and converts it to a void type. For example:
int y; y = 3; rvalue y = 3 becomes void
If you write an accessible lvalue expression in an rvalue subcontext (such as a test or value context), the translator accesses the value stored in the object to determine the result. A qualified type (const or volatile) becomes its corresponding unqualified type. For example:
const int x; int y; y = x; const int lvalue x becomes int rvalue
If you write an array lvalue expression in an rvalue subcontext, the translator converts the type array of T to pointer to T. The value of the expression is the address of the first element of the array. For example:
int a[10], *pi; pi = a; array a becomes pointer rvalue
If you write a function-designator expression in an rvalue subcontext, the translator converts the type function returning T to pointer to function returning T. The value of the expression is the address of the function. For example:
int f(void); int (*pf)(void); pf = f; function f becomes pointer rvalue
Within several contexts the translator converts the type of a scalar expression (or subexpression). The conversions are called:
This section describes each of these conversions and the context in which it occurs. It also shows how the translator determines the value of the converted type from the value of the original type.
Except when it is the operand of the sizeof operator, an integer rvalue expression has one of four types:
When you write an expression in an rvalue subcontext and the expression has an integer type that is not one of these types, the translator promotes its type. If all of the values representable in the original type are also representable as type int, then the promoted type is int. Otherwise, the promoted type is unsigned int.
Thus, for signed char, short, and any signed bitfield type, the promoted type is int. For each of the remaining integer types (char, unsigned char, unsigned short, any plain bitfield type, or any unsigned bitfield type), the effect of these rules is to favor promoting to int wherever possible, but to promote to unsigned int if necessary to preserve the original value in all possible cases.
For example:
signed char ch; unsigned short us, f(char *, ...); f("%i%x", ch, ch becomes int us); us becomes int or unsigned int
When you write an infix operator that has two arithmetic rvalue operands, the translator frequently determines the type of the result by balancing the types of the two operands. To balance two types, the translator applies the following rules to the promoted types of the operands:
Each of the operands is converted to the balanced type, the arithmetic operation occurs between the now identical types, and the result of the operation has the balanced type. For example:
int i; long lo; double d; return ((i + lo) i becomes long + d); (i + lo) becomes double
You store a value in an object by writing an expression that
contains an assigning operator. The assigning operators are
=
,
*=
,
/=
,
%=
,
+=
,
-=
,
<<=
,
>>=
,
&=
,
^=
, and
|=
.
If the type of the aalue to be stored by an assigning operator is
compatible
with the type of the object, the program stores the
value unmodified. Otherwise, the translator determines the appropriate
conversion to perform before storing the new value.
You can also specify a type conversion by writing a
type cast operator.
You can specify any type conversion permitted for
an assigning operator, plus several other conversions between scalar
types.
The translator defines a number of conversions between scalar
types that you can specify by assigning or type casting. It does not
define all possible conversions, however:
- You can convert any
arithmetic
(integer or floating-point) type
to any other arithmetic type. The conversion preserves the original
value, wherever possible. Otherwise, the value changes
with the representation
as described later in this section.
- You can convert any
pointer type to an
integer type, but the
result is always
implementation
defined.
You cannot convert a pointer type to a
floating-point type.
- You can convert any integer type to any pointer type.
The value zero yields a
null pointer value.
Any nonzero value yields an implementation-defined
result. You cannot convert a floating-point type to a pointer type.
- You can convert any object pointer or pointer to incomplete
type to any other object pointer or pointer to incomplete type. The
result is implementation defined, however, unless the original pointer
is suitably aligned for use as the resultant pointer. You can safely
convert any such pointer to a pointer to a character type (or a pointer
to void, which has the same repeesentation). You can use such a
pointer to character to access the first byte of the object as a character.
If you then convert that pointer to a type
compatible
with the original pointer, it will equal
the original pointer and you can use the pointer
to access the object. For example:
int i;
char *pc = (char *)&i; valid type cast
*(int *)pc = 3; also valid
if (*pc == *(int *)pc) also valid
printf("int stores l.s. byte first\n");
- You can convert a pointer to any
function type to a pointer
to any other function type. If you then convert that pointer to a
type compatible with the original pointer, it will equal the original
pointer and you can use the pointer to call the function. For example,
the following two type casts are both valid:
extern int sum(int, int);
void (*pv)(void) = (void (*)(void))∑
if (((int (*)(int, int))pv)(1, 2) == 3)
printf("sum was called properly\n");
The following table summarizes all possible
scalar conversions. Note that you can convert any scalar type to any
other scalar type by specifying no more than two conversions. In many
cases, however, at least one of the conversions is
implementation
defined.
Pointer to
From: Arithmetic Incomplete Pointer to
To: Type or Object Function
Arithmetic any to integer to integer
Type only only
Pointer to from integer any ---
Incomplete or only
Object
Pointer to from integer --- any
Function only
Changing Representations
When you convert between any two arithmetic types, what happens
to the value depends on the number of bits used to represent the original
and final types. The following table summarizes
all possible conversions between arithmetic types.
The table assumes that:
- A signed integer value
X
occupying N
bits can
represent (at least) all integers in the open-ended interval
(-2^(N-1), 2^(N-1))
.
- An unsigned integer value
X
occupying N
bits can represent all integers in the half-open interval
[0, 2^N)
(and no others).
- A floating-point value
X
can be characterized as having
N
bits reserved for representing sign and magnitude, so it
can exactly represent (at least) all integers in the open-ended interval
(-2^(N-1), 2^(N-1))
.
Conversion N < M N == M N > M
signed integer discard m.s. same value same value
to M-N bits
signed integer (can overflow)
unsigned integer if (X < 2^(N-1)) if (X < 2^(N-1)) same value
to same value same value
signed integer else impl.-def. else impl.-def.
(can overflow) (can overflow)
floating-point if (|X| < 2^(N-1)) if (|X| < 2^(N-1)) if (|X| < 2^(N-1))
to trunc(X) trunc(X) trunc(X)
signed integer else impl.-def. else impl.-def. else impl.-def.
(can overflow) (can overflow) (can overflow)
signed integer if (0 <= X) if (0 <= X) if (0 <= X)
to X % 2^N same value same value
unsigned integer else impl.-def. else X + 2^N else X + 2^N
unsigned integer X % 2^N same value same value
to
unsigned integer
floating-point if (0 <= X < 2^N) if (0 <= X < 2^N) if (0 <= X < 2^N)
to trunc(X) trunc(X) trunc(X)
unsigned integer else impl.-def. else impl.-def. else impl.-def.
(can overflow) (can overflow) (can overflow)
signed integer keep sign, same value same value
to keep m.s.
floating-point N-1 bits
unsigned integer + sign, + sign, same value
to keep m.s. keep m.s.
floating-point N-1 bits N-1 bits
floating-point keep m.s. same value same value
to N-1 bits
floating-point (can overflow)
The table shows what happens when you convert an M
-bit
representation with value X
to an N
-bit representation,
for the three cases where M
is less than, equal to, or greater
than N.
The abbreviations used in this table are:
impl.-def.
--
implementation
defined
m.s.
-- most significant
trunc(X)
-- the integer part of X
,
truncated toward zero
X % Y
-- the non-negative remainder after dividing
X
by Y
Pointer Arithmetic
You can add an integer to a value of type pointer to object.
If the value of the pointer is the address of an array element, then
adding one to the value yields the address of the next array element.
Thus, for a pointer p
to any object:
(char *)(p + 1) is identical to (char *)p + sizeof (*p)
If the value of p
is the address of the first element
in an array object, then *(p + n)
designates element number
n
(counting from zero).
If the value of p
is the address
of the last element in an array object, then (p + 1)
is a valid
address, even though *(p + 1)
is not an accessible lvalue.
You can perform pointer arithmetic on (p + 1)
just as you can
on the address of any of the elements of the array. If you form any
other address that does not designate an element of the array object
(by adding an integer to a pointer), the result is undefined.
Reading Expressions
You compose an expression from one or more
terms and zero or more operators:

Each term has a well defined
type and
class. If an expression
consists of a single term without operators, then the type and class
of the expression are the type and class of the term.
Each operator requires one, two, or three
operands. An
operand is a subexpression that can itself (generally) contain additional
operators as well as terms. If you write an expression with one or
more terms and a single operator, then the terms must be the operands
of the operator. Each operator accepts operands with only certain
combinations of types and classes. The types and classes of the operands
determine the type and class of the expression containing the operator.
If you write an expression with one or more terms and two operators,
then the translator must determine which terms to
group with which operators.
You can enclose any subexpression in parentheses to make
clear that it groups as a single operand. Such parentheses have no
other effect than to control grouping. If you do not write such parentheses,
however, the translator applies a number of
precedence rules
to determine how the expression groups. Every expression you write
groups in only one way.
This section describes how to determine the type and class of
any term. Later sections in this documents explain the rules for grouping
operands in the presence of two or more operators, the effect of each
operator, what operands it accepts, and what result it produces.
A term takes one of several forms:

A name
in this context be declared as one of three entities:
- a function
- an object (possibly with
incomplete type)
- an enumeration
constant
For a function, the name is a
function-designator expression
with the declared type. For an object, the name is an
lvalue expression
with the declared type. For an enumeration constant, the name is an
rvalue expression with type int.
(You can write a
type definition or
tag in an expression only as part of a
type-name declaration
enclosed in parentheses, to make a
type cast operator, or as the operand of a
sizeof operator.)
If no declaration is visible for one of these entities and if
you write a left parenthesis immediately following the name,
then the translator
implicitly declares
the name in the current name space
as a function returning int without any argument information.
An integer constant is an
rvalue expression whose type
depends on the value, the base, and any suffix you write.
In all cases, the value
is the sequence of digits taken as a number to the specified base.
Each base and suffix combination determines a sequence of possible types.
The translator selects the earliest type
in the sequence that can represent the value
of the particular integer constant.
For a
decimal integer constant,
the sequences are:
- no suffix -- int, long, unsigned long
U
suffix -- unsigned int, unsigned long
L
suffix -- long, unsigned long
UL
suffix -- unsigned long
For an
octal or
hexadecimal integer constant,
the sequences are:
- no suffix -- int, unsigned int, long, unsigned long
U
suffix -- unsigned int, unsigned long
L
suffix -- long, unsigned long
UL
suffix -- unsigned long
For example, if type int has a 16-bit representation:
70 070 0x70 all type int
7000U 070U 0x7000 all type unsigned
70000 070L 0x700L all type long
A
floating-point
constant is an
rvalue expression whose
type depends on any suffix you write:
- no suffix -- double
F
suffix -- float
L
suffix -- long double
Its value
is the integer plus fraction parts, taken as a decimal number,
times ten raised to the exponent part, taken as a signed decimal
number.
A
character constant is an
rvalue expression.
If you specify just one character in
the character constant, its type depends on any prefix you write:
- no prefix -- int
L
prefix -- the type wchar_t
promoted
Its value
is the code for the character you specify in the
target character set.
If you specify other than one character in a character constant,
the type and value are
implementation
defined.
A
string literal is an
lvalue expression whose type depends
on the number of characters you specify and any prefix you write:
- no prefix -- array of char with repetition count N
L
prefix -- array of wchar_t
with repetition count N
N is one more than the number of characters you specify
when you write the string (for the terminating null character).
Its stored value
is the sequence of codes for the character(s) you specify in the
target character set, followed by the value zero. For example:
"hello" type is array of 6 char
L"hai" type is array of 4 wchar_t
The term
sizeof (declaration)
is an rvalue expression of type size_t
,
with value as described for that operator.
Any expression you write enclosed in
parentheses is a term whose
type, class, and value are the same as for the expression. Enclosing
an expression in parentheses has no effect other than to control grouping.
Grouping
In the absence of parentheses, the translator groups
operators with operands in the following order:
1. The translator applies a postfix operator
immediately following a term before it applies any other operator.
It then applies any postfix operators to the right of that operator,
grouping from left to right.

2. The translator applies a prefix operator
immediately preceding a term and any postfix operators.
It then applies any prefix operators to the
left of that operator, grouping from right to left.

3. The translator applies infix operators
in descending order of precedence.
Operators at the same order of precedence group either from left to
right or from right to left, as indicated for the particular precedence
level.

The translator resolves two ambiguities by:
- always interpreting
sizeof (declaration)
as a term (never as the
sizeof operator followed by a
type cast operator)
- always interpreting a comma within a
function call argument
list as an argument expression separator (never as a
comma operator
within an argument expression).
In either case, you can use parentheses to obtain the alternate
grouping.
The following table shows all operators grouped by
precedence level in descending order.
Operator Notation Grouping
postincrement X++ from left to right
postdecrement X--
subscript X[Y]
function call X(Y)
select member X.Y
point at member X->Y
sizeof sizeof X from right to left
preincrement ++X
predecrement --X
address of &X
indirection *X
plus +X
minus -X
bitwise NOT ~X
logical NOT !X
type cast (declaration)X
multiply X * Y from left to right
divide X / Y
remainder X % Y
add X + Y from left to right
subtract X - Y
left shift X << Y from left to right
right shift X >> Y
less than X < Y from left to right
less than or equal X <= Y
greater than X > Y
greater than or equal X >= Y
equals X == Y from left to right
not equals X != Y
bitwise AND X & Y from left to right
bitwise exclusive OR X ^ Y from left to right
bitwise inclusive OR X | Y from left to right
logical AND X && Y from left to right
logical OR X || Y from left to right
conditional Z ? X : Y from right to left
assignment X = Y from right to left
multiply assign X *= Y
divide assign X /= Y
remainder assign X %= Y
add assign X += Y
subtract assign X -= Y
left shift assign X <<= Y
right shift assign X >>= Y
bitwise AND assign X &= Y
bitwise exclusive OR assign X ^= Y
bitwise inclusive OR assign X |= Y
comma X, Y from left to right
The table also shows
how operators group within a given precedence level.
For example:
y = m * x + b is y = ((m * x) + b)
*p++ = -x->y is (*(p++)) = (-(x->y))
Operator Summary
This section describes every operator. It lists the operators
alphabetically by name, showing how to write each one with operands
X
, Y
, and Z
(as needed).
Following a description
of what the operator does is a table of all permissible combinations
of operand types and classes, with the type and class of the result
for each combination.
Some expressions produce a result that has an integer type that
varies among implementations. Each of these types has a type definition
that you can include in the program by including the standard header
<stddef.h>
.
The type definitions are:
ptrdiff_t
(which is the type of the
subtract operator
when its operands are both pointers to objects)
size_t
(which is the type of the
sizeof operator)
wchar_t
(which is the type of an element of a
wide-character
string literal)
You do not have to include these type definitions in the program
to use the subtract or sizeof operators, for example,
or to write wide-character string literals.
If the result of an operation cannot be represented by a value
of the result type, then an
exceptional result occurs:
- Overflow is an exceptional result
where the value is too large to be represented by an
arithmetic type.
- Underflow is an exceptional result
where the value is too small to be represented by a
floating-point type.
- Zero divide is an exceptional result
where the result of dividing by zero has no well defined value.
If any form of exceptional result occurs,
the program behavior is undefined.
A type described below as
Q qualified is an optionally
qualified type. It can be:
- unqualified
- const qualified
- volatile qualified
- const and volatile qualified
For pointer arithmetic, every object is considered an array
object (with perhaps only one element). If the array a
has
N
elements, then a[N]
is the element immediately beyond the array.
Add Assign
X += Y
You write X += Y
to access the value stored in the object
designated by X
, add the value of Y
to the stored value, and store the new value back in the object.
Result X Y
type of X arithmetic arithmetic
rvalue modifiable lvalue rvalue
type of X pointer to object integer
rvalue modifiable lvalue rvalue
Add
X + Y
You write X + Y
to add the value of
Y
to the value of X
.
You can add an integer to a pointer value only if the
result is the address of an element within (or just beyond) the same
array object.
Result X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
type of X pointer to object integer
rvalue rvalue rvalue
type of Y integer pointer to object
rvalue rvalue rvalue
Address of
&X
You write &X
to obtain the address of the function or
object designated by X
. You cannot obtain the address of an
object declared with storage class register
.
Result X
pointer to T any object type T
rvalue except bitfield
lvalue
pointer to T incomplete type T
rvalue lvalue
pointer to T function type T
rvalue function designator
Assignment
X = Y
You write X = Y
to store
the value of Y
in the object
designated by X
. If Y
is an lvalue expression (that is converted to
an rvalue expression to obtain its stored value),
then the object it designates either must have no bytes in common
with the object designated by X
or must overlap exactly, and
the objects must have compatible types.
Result X Y
type of X arithmetic arithmetic
rvalue modifiable lvalue rvalue
type of X pointer, structure, or type of X
rvalue union type T rvalue
modifiable lvalue
type of X pointer to qualified T pointer to same or
rvalue modifiable lvalue less qualified type
compatible with T
rvalue
type of X pointer to void pointer to object
rvalue modifiable lvalue or incomplete type
rvalue
type of X pointer to object pointer to void
rvalue or incomplete type rvalue
modifiable lvalue
type of X any pointer type integer zero
rvalue modifiable lvalue rvalue
Bitwise AND Assign
X &= Y
You write X &= Y
to access
the value stored in the object
designated by X
, form the
bitwise AND of the value
of Y
with the stored value,
and store the new value back in the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Bitwise AND
X & Y
You write X & Y
to form
the bitwise AND of the values of
X
and Y
. Each bit of the result
is set if the corresponding
bits in both X
and Y
are set.
Result X Y
balanced type of X and Y integer integer
rvalue rvalue rvalue
Bitwise Exclusive OR Assign
X ^= Y
You write X ^= Y
to access the value stored in the object
designated by X
, form the
bitwise exclusive OR of the value
of Y
with the stored value, and store the new value back in
the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Bitwise Exclusive OR
X ^ Y
You write X ^ Y
to form the bitwise exclusive OR of the
values of X
and Y
. Each bit of the result is set if
the corresponding bits in X
and Y
differ.
Result X Y
balanced type of X and Y integer integer
rvalue rvalue rvalue
Bitwise Inclusive OR Assign
X |= Y
You write X |= Y
to access the value stored in the object
designated by X
, form the
bitwise inclusive OR of the value
of Y
with the stored value, and store the new value back in
the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Bitwise Inclusive OR
X | Y
You write X | Y
to form the bitwise inclusive OR of the
values of X
and Y
. Each bit of the result is set if
either of the corresponding bits in
X
or Y
is set.
Result X Y
balanced type of X and Y integer integer
rvalue rvalue rvalue
Bitwise NOT
~X
You write ~X
to form the bitwise NOT of the value of
X
. Each bit of the result is set if the corresponding bit in
X
is not set.
Result X
type of X integer
rvalue rvalue
Comma
X, Y
You write X, Y
to first evaluate X
as a
side-effects context
expression and then to evaluate Y
. There is a
sequence point
between the evaluation of the two operands.
Result X Y
type of Y void expression any
rvalue rvalue
Conditional
Z ? X : Y
You write Z ? X : Y
to evaluate
one of the operands X
or Y
,
depending on the value of the
test-context expression
Z
, which must be a scalar rvalue.
If Z
has a nonzero value,
then only X
is evaluated; otherwise, only Y
is
evaluated. The value of the expression is the value of the operand
that is evaluated, converted to the result type. A
sequence point
occurs between the evaluation of Z
and the evaluation of X
or Y
.
Result X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
type of X structure or union type of X
rvalue rvalue rvalue
type of X pointer to T integer zero
rvalue rvalue rvalue
type of Y integer zero pointer to T
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void void object or
rvalue rvalue incomplete type
rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void object or void
rvalue incomplete type rvalue
rvalue
void expression void expression void expression
Divide Assign
X /= Y
You write X /= Y
to access the value stored in the object
designated by X
, divide that value by the value of Y
,
and store the new value back in the object. (See the Divide
operator following.)
Result X Y
type of X arithmetic arithmetic
rvalue modifiable lvalue rvalue
Divide
X / Y
You write X / Y
to divide the value of X
by the
value of Y
. Dividing by by zero yields an
exceptional result.
For integer types, a positive quotient truncates toward zero.
Result X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
Equals
X == Y
You write X == Y
to test whether the value of X
equals the value of Y
. The result is an int rvalue whose
value is 1 if the test is successful; otherwise, the value is zero.
Eachoof the operands is converted to a common test type for the comparison.
The table below shows the test type, rather than the result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
type of X pointer to T integer zero
rvalue rvalue rvalue
type of Y integer zero pointer to T
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void void object or
rvalue rvalue incomplete type
rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void object or void
rvalue incomplete type rvalue
rvalue
Function Call
X(Y)
You write X(Y)
to call a function.
The value of the expression
(if any) is the value that the function returns. A
sequence point
occurs after the program evaluates X
and Y
and before it
calls the function.
Result X Y
object type T function returning T zero or more
rvalue function designator argument rvalues
object type T pointer to function zero or more
rvalue returning T argument rvalues
rvalue
void expression function returnigg zero or more
void argument rvalues
function designator
void expression pointer to function zerooor more
returning void argument rvalues
rvalue
Greater Than Or Equal
X >= Y
You write X >= Y
to test
whether the value of X
is greater than or equal to the value of Y
. The result is an
int rvalue whose value is 1 if the test is successful; otherwise,
the value is zero. Each of the operands is converted to a common test
type for the comparison. You can compare two pointer values only if
they are the addresses of elements within (or just beyond) the same
array object. The table below shows the test type, rather than the
result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
Greater Than
X > Y
You write X > Y
to test
whether the value of X
is
greater than the value of Y
. The result is an int rvalue
whose value is 1 if the test is successful; otherwise, the value is
zero. Each of the operands is converted to a common test type for
the comparison. You can compare two pointer values only if they are
the addresses of elements within (or just beyond) the same array object.
The table below shows the test type, rather than the result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
Indirection
*X
You write *X
to use the value of the pointer X
to designate an entity. The address of the entity is the value of
the pointer.
Result X
type T pointer to object type T
lvalue rvalue
type T pointer to function type T
function designator rvalue
void expression pointer to void
rvalue
Left Shift Assign
X <<= Y
You write X <<= Y
to access the value stored in the object
designated by X
,
shift that value to the left by the number
of bit positions specified by the value of Y
, and store the
new value back in the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Left Shift
X << Y
You write X << Y
to shift the value of X
to the
left by the number of bit positions
specified by the value of Y
.
For an N
-bit representation for the
promoted value of X
,
the value of Y
must be in the interval
[0, N)
. Zeros
fill the vacated bit positions.
Result X Y
type of X integer integer
rvalue rvalue rvalue
Less Than Or Equal
X <= Y
You write X <= Y
to test whether the value of X
is less than or equal to the value of Y
.
The result is an int
rvalue whose value is 1 if the test is successful; otherwise, the
value is zero. Each of the operands is converted to a common test
type for the comparison. You can compare two pointer values only if
they are the addresses of elements within (or just beyond) the same
array object. The table below shows the test type, rather than the
result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
Less Than
X < Y
You write X < Y
to test
whether the value of X
is
less than the value of Y
. The result is an int rvalue
whose value is 1 if the test is successful; otherwise, the value is
zero. Each of the operands is converted to a common test type for
the comparison. You can compare two pointer values only if they are
the addresses of elements within (or just beyond) the same array object.
The table below shows the test type, rather than the result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
Logical AND
X && Y
You write X && Y
to test whether both of the operands X
and Y
are nonzero. If X
is zero,
then only X
is evaluated and the value
of the expression is zero; otherwise, Y
is evaluated and the value of the expression is 1
if Y
is nonzero or zero if Y
is zero.
A sequence point occurs between the evaluation
of X
and the evaluation of Y
.
Result X Y
int scalar scalar
rvalue rvalue rvalue
Logical NOT
!X
You write !X
to test
whether X
is zero. If X
is zero, the value of the expression is 1; otherwise, the value is
zero.
Result X
int scalar
rvalue rvalue
Logical OR
X || Y
You write X || Y
to test whether either of the operands
X
or Y
is nonzero.
If X
has a nonzero value,
then only X
is evaluated and the value of the expression is
1; otherwise, Y
is evaluated and the value of the expression
is 1 if Y
is nonzero or zero if Y
is zero. A
sequence point
occurs between the evaluation of X
and the evaluation
of Y
.
Result X Y
int scalar scalar
rvalue rvalue rvalue
-X
You write -X
to negate the value of X
.
Result X
type of X arithmetic
rvalue rvalue
Multiply Assign
X *= Y
You write X *= Y
to access the value stored in the object
designated by X
, multiply
that value by the value of Y
,
and store the new value back in the object.
Result X Y
type of X arithmetic arithmetic
rvalue modifiable lvalue rvalue
Multiply
You write X * Y
to multiply
the value of X
by the
value of Y
.
Result X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
Not Equals
X != Y
You write X != Y
to test whether the value of X
does not equal the value of Y
. The result is an int
rvalue whose value is 1 if the test is successful; otherwise, the
value is zero. Each of the operands is converted to a common test
type for the comparison. The table below shows the test type, rather
than the result type.
Test X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
type of X pointer to T integer zero
rvalue rvalue rvalue
type of Y integer zero pointer to T
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
composite of T and T' type T compatible type T'
rvalue rvalue rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void void object or
rvalue rvalue incomplete type
rvalue
pointer to pointer to pointer to
Q and Q' qualified Q qualified Q' qualified
void object or void
rvalue incomplete type rvalue
rvalue
Plus
+X
You write +X
to leave
the value of X
unchanged.
(You use this operator primarily to emphasize
that a term is not negated.)
Result X
type of X arithmetic
rvalue rvalue
Point at
X->Y
You write X->Y
to select
the member whose name is Y
from the structure or union whose address
is the value of X
.
Result X Y
type of member Y pointer to member name within
lvalue structure or union structure or union
rvalue
Postdecrement
X--
You write X--
to access the value stored in the object
designated by X
, subtract 1 from the value, and store the new
value back in the object. The value of the expression is the original
value stored in the object.
Result X
type T scalar type T
rvalue modifiable lvalue
Postincrement
X++
You write X++
to access the value stored in the object
designated by X
, add 1 to the value, and store the new value
back in the object. The value of the expression is the original
value stored in the object.
Result X
type T scalar type T
rvalue modifiable lvalue
Predecrement
--X
You write --X
to access the value stored in the object
designated by X
, subtract 1 from the value, and store the new
value back in the object. The value of the expression is the final
value stored in the object.
Result X
type T scalar type T
rvalue modifiable lvalue
Preincrement
++X
You write ++X
to access the value stored in the object
designated by X
, add 1 to the value, and store the new value
back in the object. The value of the expression is the final
value stored in the object.
Result X
type T scalar type T
rvalue modifiable lvalue
Remainder Assign
X %= Y
You write X %= Y
to access the value stored in the object
designated by X
, divide
that value by the value of Y
, and store the
remainder back in the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Remainder
X % Y
You write X % Y
to compute the remainder of the value of
X
divided by the value of Y
.
Dividing by by zero yields an
exceptional result.
Barring overflow or division by zero, it is always true that
X = (X / Y) * Y + (X % Y)
.
Result X Y
balanced type of X and Y integer integer
rvalue rvalue rvalue
Right Shift Assign
X >>= Y
You write X >>= Y
to access
the value stored in the object designated by X
,
shift that value to the right by the number
of bit positions specified by the value of Y
, and store the
new value back in the object.
Result X Y
type of X integer integer
rvalue modifiable lvalue rvalue
Right Shift
X >> Y
You write X >> Y
to shift the value of X
to the
right by the number of bit positions
specified by the value of Y
.
For an N
-bit representation
for the value of X
, the
(promoted) value of Y
must be in the interval
[0, N)
.
If X
is nonnegative, then zeros fill the vacated bit positions;
otherwise, the result is
implementation
defined.
Result X Y
balanced type of X and Y integer integer
rvalue rvalue rvalue
Select
X.Y
You write X.Y
to select the member Y
from the
structure or union X
. The result is an lvalue expression only
if X
is an lvalue expression.
Result X Y
type of member Y structure or union member name within
lvalue lvalue the structure or union
type of member Y structure or union member name within
rvalue rvalue the structure or union
sizeof
sizeof X
You write sizeof X
to determine the size in bytes of
an object whose type is the type of X
.
Do not write a function-designator expression for X
.
The translator uses the expression you write
for X
only to determine a type;
it is not evaluated. The operand X
is otherwise not considered a part of the expression containing
the sizeof operator. Therefore, prohibitions on what can be
in an expression (such as an arithmetic constant expression) do not
apply to any part of X
.
Result X
size_t object type
rvalue lvalue
size_t object type
rvalue rvalue
Subscript
X[Y]
You write X[Y]
to designate an array element. The operator
is identical in effect to *((X)+(Y))
.
Typically, X
is
an array lvalue expression (which becomes a pointer rvalue expression)
or an rvalue expression of some pointer type whose value is the address
of an array element. In this case, Y
is an integer rvalue.
The designated array element is Y
elements away from the element
designated by X
. Because of the symmetry of the two operands,
however, you can write them in either order.
Result X Y
object type T pointer to T integer
lvalue rvalue rvalue
object type T integer pointer to T
lvalue rvalue rvalue
Subtract Assign
X -= Y
You write X -= Y
to access the value stored in the object
designated by X
,
subtract the value of Y
from the value,
and store the new value back in the object.
Result X Y
type of X arithmetic arithmetic
rvalue modifiable lvalue rvalue
type of X pointer to object integer
rvalue modifiable lvalue rvalue
Subtract
X - Y
You write X - Y
to subtract the value of Y
from
the value of X
. You can subtract two pointer values only if
they are the addresses of elements within (or just beyond) the same
array object. The result tells you how many elements lie between the
two addresses.
Result X Y
balanced type of X and Y arithmetic arithmetic
rvalue rvalue rvalue
type of X pointer to object integer
rvalue rvalue rvalue
ptrdiff_t pointer to Q qualified pointer to Q' qualified
rvalue object type T compatible type T'
rvalue rvalue
Type Cast
(decl)X
You write (decl)X
to
convert the value of X
to the scalar (or void) type T that you specify in the
type-name declaration decl
enclosed in parentheses. The table
below shows valid combinations of declared type and operand type.
Result Type T X
type T integer scalar
rvalue rvalue
type T floating-point arithmetic
rvalue rvalue
type T pointer to integer
rvalue any type rvalue
type T pointer to object pointer to object
rvalue or incomplete type or incomplete type
rvalue
type T pointer to pointer to
rvalue function function
rvalue
void expression void scalar rvalue or
void expression
Order of Evaluation
When the program evaluates an expression, it has considerable
latitude in choosing the order in which it evaluates subexpressions.
For example, the translator can alter:
y = *p++;
either to:
temp = p; p += 1; y = *temp;
or to:
y = *p; p += 1;
As another example, the program can evaluate the expression
f() + g()
by calling the functions in either order.
The order of evaluation is important in understanding when
side effects occur,
such as storing a value in an object.
A sequence point
is a point in the program at which you
can determine which side effects have occurred and which have yet
to take place. Each of the expressions you write as part of a statement,
for example, has a sequence point at the end of it. You can be sure
that for:
y = 37;
x += y;
the program stores the value 37 in y
before it accesses
the value stored in y
to add it to the value stored in x
.
Sequence points can also occur within expressions. The
comma,
conditional,
function call,
logical AND, and
logical OR
operators each contain a sequence point. For example, you can write:
if ((c = getchar()) != EOF && isprint(c))
and know that the program evaluates isprint(c)
only after
a new value is stored in c
from the call to getchar
.
Between two sequence points, you must access the value stored
in an object whose contents you are altering only to determine the
new value to store, and store a value in an object no more than once.
For example:
val = 10 * val
+ (c - '0'); well defined
i = ++i + 2; NOT well defined
An expression can contain sequence points and still not have
a definite order of evaluation. In the example above, the expression
f() + g()
contains a sequence point before each function call,
but the add operator imposes no ordering on the evaluation
of its operands.
See also the
Table of Contents and the
Index.
Copyright © 1989-1996
by P.J. Plauger and Jim Brodie. All rights reserved.